#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "ockam/rs/vault.h"

int main(void) {
    const int32_t bytes = 32;
    const int32_t p256_bytes = 65;
    ockam_vault_t vault;
    ockam_vault_secret_t secret_25519;
    uint8_t* p_secret_25519;
    uint8_t* p_public_25519;
    size_t secret_25519_bytes;
    size_t public_25519_bytes;
    ockam_vault_secret_t secret_p256;
    uint8_t* p_secret_p256;
    uint8_t* p_public_p256;
    size_t secret_p256_bytes;
    size_t public_p256_bytes;
    ockam_vault_secret_attributes_t attributes;
    uint32_t result;
    uint8_t* p_random_bytes;
    uint8_t* p_sha256_digest;
    int32_t i;
    char sep[3] = {' ', ' ', '\0'};

    result = ockam_vault_default_init(&vault);
    if (0 != result) {
        printf("Failed to initialize the vault: %d\n", result);
        return 1;
    }

    p_random_bytes = (uint8_t*) malloc(sizeof(uint8_t) * bytes);
    result = ockam_vault_random_bytes_generate(vault, p_random_bytes, bytes);
    if (0 != result) {
        printf("Failed to generate random bytes: %d\n", result);
        return 1;
    }
    printf("Generate random bytes ...");
    fflush(stdout);
    sep[0] = ' ';
    sep[1] = '[';
    for (i = 0; i < bytes; i++) {
        printf("%s%d", sep, p_random_bytes[i]);
        result ^= p_random_bytes[i];
        sep[0] = ',';
        sep[1] = ' ';
    }
    printf("]");
    fflush(stdout);

    if (0 == result) {
        printf("...fail. Generated bytes are zero\n");
        goto Fail;
    } else {
        printf("...pass.\n");
    }

    printf("Compute SHA-256 digest...");
    p_sha256_digest = (uint8_t*) malloc(sizeof(uint8_t) * bytes);
    result = ockam_vault_sha256(vault, p_random_bytes, bytes, p_sha256_digest);
    if (0 != result) {
        printf("Failed to compute digest: %d\n", result);
        return 1;
    }
    sep[0] = ' ';
    sep[1] = '[';
    for (i = 0; i < bytes; i++) {
        printf("%s%d", sep, p_sha256_digest[i]);
        result ^= p_sha256_digest[i];
        sep[0] = ',';
        sep[1] = ' ';
    }
    printf("]");
    fflush(stdout);

    if (0 == result) {
        printf("...fail. Computed SHA-256 digest is zero\n");
        goto Fail;
    } else {
        printf("...pass.\n");
    }

    printf("Generate secret key...");
    fflush(stdout);

    attributes.type = OCKAM_VAULT_SECRET_TYPE_CURVE25519_PRIVATEKEY;
    attributes.purpose = OCKAM_VAULT_SECRET_PURPOSE_KEY_AGREEMENT;
    attributes.persistence = OCKAM_VAULT_SECRET_EPHEMERAL;

    result = ockam_vault_secret_generate(vault, &secret_25519, attributes);

    if (0 != result) {
        printf("fail: %d.\n", result);
        goto Fail;
    }
    attributes.type = OCKAM_VAULT_SECRET_TYPE_P256_PRIVATEKEY;
    result = ockam_vault_secret_generate(vault, &secret_p256, attributes);

    if (0 != result) {
        printf("fail: %d.\n", result);
        goto Fail;
    } else {
        printf("pass.\n");
    }

    printf("Get public keys...");
    fflush(stdout);

    p_public_25519 = (uint8_t*) malloc(sizeof(uint8_t) * bytes);
    result = ockam_vault_secret_publickey_get(vault, secret_25519, p_public_25519, bytes, &public_25519_bytes);
    if (0 != result) {
        printf("fail. Unable to export 25519 public key: %d\n", result);
        goto Fail;
    }
    printf("\n    public key 25519:");
    sep[0] = ' ';
    sep[1] = '[';
    for (i = 0; i < bytes; i++) {
        printf("%s%d", sep, p_public_25519[i]);
        result ^= p_public_25519[i];
        sep[0] = ',';
        sep[1] = ' ';
    }
    printf("]\n");
    fflush(stdout);

    if (0 == result) {
        printf("    fail. public key 25519 is all zero.\n");
        goto Fail;
    }

    p_public_p256 = (uint8_t*) malloc(sizeof(uint8_t) * p256_bytes);
    result = ockam_vault_secret_publickey_get(vault, secret_p256, p_public_p256, p256_bytes, &public_p256_bytes);
    if (0 != result) {
        printf("fail. Unable to export P-256 public key: %d\n", result);
        goto Fail;
    }

    printf("    public key P-256:");
    sep[0] = ' ';
    sep[1] = '[';
    for (i = 0; i < p256_bytes; i++) {
        printf("%s%d", sep, p_public_p256[i]);
        result ^= p_public_p256[i];
        sep[0] = ',';
        sep[1] = ' ';
    }
    printf("]\n");
    fflush(stdout);

    if (0 == result) {
        printf("    fail. public key P-256 is all zero.\n");
        goto Fail;
    } else {
        printf("    pass.\n");
    }

    printf("Export secret keys...");
    fflush(stdout);

    p_secret_25519 = (uint8_t*) malloc(sizeof(uint8_t) * bytes);
    result = ockam_vault_secret_export(vault, secret_25519, p_secret_25519, bytes, &secret_25519_bytes);
    if (0 != result) {
        printf("fail. Unable to export 25519 secret key: %d\n", result);
        goto Fail;
    }
    printf("\n    secret key 25519:");
    sep[0] = ' ';
    sep[1] = '[';
    for (i = 0; i < bytes; i++) {
        printf("%s%d", sep, p_secret_25519[i]);
        result ^= p_secret_25519[i];
        sep[0] = ',';
        sep[1] = ' ';
    }
    printf("]\n");
    fflush(stdout);

    if (0 == result) {
        printf("    fail. secret key 25519 is all zero.\n");
        goto Fail;
    }

    p_secret_p256 = (uint8_t*) malloc(sizeof(uint8_t) * bytes);
    result = ockam_vault_secret_export(vault, secret_p256, p_secret_p256, bytes, &secret_p256_bytes);
    if (0 != result) {
        printf("fail. Unable to export P-256 secret key: %d\n", result);
        goto Fail;
    }

    printf("    secret key P-256:");
    sep[0] = ' ';
    sep[1] = '[';
    for (i = 0; i < bytes; i++) {
        printf("%s%d", sep, p_secret_p256[i]);
        result ^= p_secret_p256[i];
        sep[0] = ',';
        sep[1] = ' ';
    }
    printf("]\n");
    fflush(stdout);

    if (0 == result) {
        printf("    fail. secret key P-256 is all zero.\n");
        goto Fail;
    } else {
        printf("    pass.\n");
    }

Fail:
    free(p_secret_25519);
    free(p_public_25519);
    free(p_secret_p256);
    free(p_public_p256);
    free(p_random_bytes);
    free(p_sha256_digest);

    ockam_vault_deinit(vault);
}
